package com.healthcare.apm.category.tool;

import com.alibaba.fastjson.JSON;
import com.healthcare.apm.category.util.IKAnalyzer4Lucene7;
import com.healthcare.apm.category.model.DataTypeConfig;
import com.healthcare.apm.category.model.OutputField;
import lombok.Setter;
import org.apache.logging.log4j.util.Strings;
// 移除未使用的XMLBeans导入
import org.springframework.util.PropertyPlaceholderHelper;

import java.util.*;
import java.util.stream.Collectors;

public class BayesianCategoryTrainer implements CategoryTraining{


    @Setter
    private JDBCCategoryTools jdbcCategoryTools;

    @Setter
    private DataTypeConfig dataTypeConfig;

    private final String DATA_TABLE_CREATE_SQL = "CREATE TABLE %s (id integer not null  constraint %s_pk primary key autoincrement, %s);";
    private final String DETAIL_TABLE_CREATE_SQL = "CREATE TABLE %s (id integer not null  constraint %s_pk primary key autoincrement," +
            " source_data TEXT,type TEXT,field_name TEXT,detail TEXT, %s);";


    private String sourceTableName;
    private String detailTableName;

    private String insertSqlTemplate = "INSERT INTO %s (%s) VALUES (%s)";

    private String computeSqlTemplate = "with probTable as(select COUNT(*) over (PARTITION by detail, type,field_name, ${outputFieldName} ) * 1.0 " +
            " / COUNT(*) over (PARTITION by detail, type,field_name ) as prob, ${outputFieldName}, detail, TYPE, field_name from" +
            " ${detailTableName} d) " +
            "update ${detailTableName} " +
            "set ${outputFieldName}_prob = probTable.prob " +
            "from probTable where " +
            " ${detailTableName}.type = probTable.type " +
            " and ${detailTableName}.detail = probTable.detail " +
            " and ${detailTableName}.field_name = probTable.field_name " +
            " and ${detailTableName}.${outputFieldName} = probTable.${outputFieldName} ;";

    @Override
    public void init() {

        sourceTableName = String.format("data_%s", dataTypeConfig.getTypeName());
        detailTableName = String.format("detail_%s", dataTypeConfig.getTypeName());
        buildTables();
    }


    @Override
    public void readData(List<Map<String, String>> data) {
        data.forEach(i->{
            insertSourceData(i);
            insertDetailData(i);
        });

    }

    @Override
    public void training() {
        dataTypeConfig.getOutputFields().forEach(outputField->{
            updateTrainingData(outputField.getFiledName());
        });
    }

    @Override
    public void saveData(String filedName) {

    }


    private void  buildTables(){

        //create source table
        List<String> fields = new ArrayList<>();
        dataTypeConfig.getInputFields().stream().map(i-> i.getFiledName() + " TEXT ").forEach(fields::add);
        dataTypeConfig.getOutputFields().stream().map(i-> i.getFiledName() + " TEXT ").forEach(fields::add);
        String tableFields = String.join(",", fields);
        String sql = String.format(DATA_TABLE_CREATE_SQL, sourceTableName,sourceTableName, tableFields);
        jdbcCategoryTools.excuteSql(sql);


        //create detail table
        List<String> detaileFields = new ArrayList<>();
        dataTypeConfig.getOutputFields().forEach(f->{
            detaileFields.add(f.getFiledName()+ " TEXT ");
            detaileFields.add(f.getFiledName()+ "_prob NUMERIC ");
        });
        sql = String.format(DETAIL_TABLE_CREATE_SQL, detailTableName,detailTableName, String.join(",", detaileFields));
        jdbcCategoryTools.excuteSql(sql);

    }

    private void insertSourceData(Map<String,String> record){
        String sql = buildInsertSQL(sourceTableName, record);
        jdbcCategoryTools.excuteSql(sql);
    }



    private void insertDetailData(Map<String, String> record) {

        List<Map<String,String>> details = new ArrayList<>();

        Map<String,String> resMap = dataTypeConfig.getOutputFields().stream()
                .filter(i-> !Strings.isBlank(record.get(i.getFiledName())))
                .collect(Collectors.toMap(OutputField::getFiledName, i-> record.get(i.getFiledName())));
        resMap.put("source_data", JSON.toJSONString(record));

        dataTypeConfig.getInputFields().forEach(i->{
            switch (i.getFieldType()){
                case SingleWord:
                    if(!Strings.isBlank( record.get(i.getFiledName()))) {
                        Map<String, String> detail = new HashMap<>();
                        detail.put("type", i.getFieldType().name());
                        detail.put("field_name", i.getFiledName());
                        detail.put("detail", record.get(i.getFiledName()));
                        detail.putAll(resMap);
                        details.add(detail);
                    }
                    break;
                case SplitWords:
                    List<String> splitParts = IKAnalyzer4Lucene7.splitText(record.get(i.getFiledName()));
                    splitParts.stream().forEach(p -> {
                        Map<String, String> partDetail = new HashMap<>();
                        partDetail.put("type", i.getFieldType().name());
                        partDetail.put("field_name", i.getFiledName());
                        partDetail.put("detail",p);
                        partDetail.putAll(resMap);
                        details.add(partDetail);
                    });
                    break;
            }
        });

        details.forEach(d->{
            String sql = buildInsertSQL(detailTableName, d);
            jdbcCategoryTools.excuteSql(sql);
        });

    }

    private String buildInsertSQL(String tableName, Map<String,String> record){
        List<String> insert_fields = new ArrayList<>();
        List<String> insert_values = new ArrayList<>();
        for(Map.Entry<String,String> entry: record.entrySet()){
            if(!Strings.isBlank(entry.getValue())){
                insert_fields.add(entry.getKey());
                insert_values.add("'".concat(entry.getValue().replace("'", "’")).concat("'"));
            }
        }
        return String.format(insertSqlTemplate,tableName,String.join(",", insert_fields),String.join(",",insert_values ));
    }


    private void updateTrainingData(String outputFieldName){

        PropertyPlaceholderHelper helper = new PropertyPlaceholderHelper("${", "}");
        Properties props = new Properties();
        props.put("detailTableName", detailTableName);
        props.put("outputFieldName", outputFieldName);
        String sql  = helper.replacePlaceholders(computeSqlTemplate, props);
        jdbcCategoryTools.excuteSql(sql);
    }
}
